/*
	@File: clog.c
	@Date: 2019/05/26
	@Author: Lyrus
	@Description: 
*/

#include "clog.h"
#include "mutex.h"
#include "conf_parser.h"
#include "stacktrace.h"
#include <time.h>

#define MAX_DATE_STR 16
#define DATE_STR_FMT "%04d%02d%02d"
#define MAX_TIME_STR 20
#define TIME_STR_FMT "%04d/%02d/%02d %02d:%02d:%02d"
#define MAX_LOG_LINE 4096

static clog_conf_t g_clog_conf;

static void clog_get_curr_date(int datestr_size, char *datestr);
static void clog_get_curr_time(int timestr_size, char *timestr);

int clog_init(const char *conf_file)
{
	char log_filepath[MAX_FILE_PATH] = {0};
	char datestr[MAX_DATE_STR] = {0};

	if (!conf_file) return -1;
	memset(&g_clog_conf, 0, sizeof(clog_conf_t));
	
	clog_conf_parse(conf_file, &g_clog_conf);
	clog_init_mutex(&g_clog_conf.mutex);

	clog_get_curr_date(sizeof(datestr), datestr);
	snprintf(log_filepath, sizeof(log_filepath) - 1, "%s/%s_%s.log", g_clog_conf.logdir, datestr, g_clog_conf.logname);
	g_clog_conf.fp = fopen(log_filepath, "a+");
	if (g_clog_conf.fp == NULL)
	{
		return -1;
	}

	g_clog_conf.init = 1;
	return 0;
}

void clog_set_lvl(clog_level lvl)
{
	g_clog_conf.filterlvl = lvl;
}

static char *clog_get_level_str(clog_level level)
{
	switch (level) {
	case CLOG_DEBUG:
		return "[DEBUG]";
	case CLOG_INFO:
		return "[INFO]";
	case CLOG_WARN:
		return "[WARN]";
	case CLOG_ERROR:
		return "[ERROR]";
	case CLOG_FATAL:
		return "[FATAL]";
	default:
		return "[     ]";
	}
}

void clog_vlog(clog_level lvl, const char *func_name, int line, const char *fmt, ...)
{
	clog_st_t st;
	va_list args;
	char *level_str = NULL;
	char timestr[MAX_TIME_STR] = { 0 };
	char log_content[MAX_LOG_LINE] = { 0 };
	char log_line[MAX_LOG_LINE] = { 0 };

	if (lvl < g_clog_conf.filterlvl)
	{
		return;
	}

	va_start(args, fmt);
	vsnprintf(log_content, sizeof(log_content) - 1, fmt, args);
	va_end(args);
	clog_get_curr_time(sizeof(timestr), timestr);
	level_str = clog_get_level_str(lvl);
	snprintf(log_line, sizeof(log_line) - 1, "%s %s %s:%d | %s\n",
		level_str, timestr, func_name, line, log_content);
	
	clog_lock(&g_clog_conf.mutex);
	fwrite(log_line, sizeof(char), strlen(log_line), g_clog_conf.fp);

	if (g_clog_conf.stacktrace)
	{
		clog_write_stacktrace_info(g_clog_conf.fp, &st);
	}

	fflush(g_clog_conf.fp);
	clog_unlock(&g_clog_conf.mutex);
}

void clog_close()
{
	clog_destroy_mutex(&g_clog_conf.mutex);
	fclose(g_clog_conf.fp);
}

static void clog_get_curr_date(int datestr_size, char *datestr)
{
	time_t tt = {0};
	struct tm *curr_time = NULL;

	time(&tt);
	curr_time = localtime(&tt);
	snprintf(datestr, datestr_size - 1, DATE_STR_FMT,
		curr_time->tm_year + 1900, curr_time->tm_mon + 1, curr_time->tm_mday);
}

static void clog_get_curr_time(int timestr_size, char *timestr)
{
	time_t tt = {0};
	struct tm *curr_time = NULL;

	time(&tt);
	curr_time = localtime(&tt);
	snprintf(timestr, timestr_size - 1, TIME_STR_FMT,
		curr_time->tm_year + 1900, curr_time->tm_mon + 1, curr_time->tm_mday,
		curr_time->tm_hour, curr_time->tm_min, curr_time->tm_sec);
}
